package com.esunny.resourcevisualization.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.esunny.resourcevisualization.activiti.MoveNodeCommand;
import com.esunny.resourcevisualization.constant.ApplyProcessStatus;
import com.esunny.resourcevisualization.domain.entity.*;
import com.esunny.resourcevisualization.domain.request.ProcessDealDto;
import com.esunny.resourcevisualization.domain.response.ReturnStructure;
import com.esunny.resourcevisualization.email.MailProxy;
import com.esunny.resourcevisualization.manage.*;
import com.esunny.resourcevisualization.service.MessageService;
import com.esunny.resourcevisualization.service.ProcessService;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;

/**
 * @version 1.0.0.0
 * @ClassName ProcessServiceImpl
 * @Description 流程处理逻辑实现
 * @Author baochencong
 * @Date 2020/11/24 14:13
 */

@Service
public class ProcessServiceImpl implements ProcessService {

    @Resource
    private SystemConfigManage systemConfigManage;

    @Resource
    private UserManage userManage;

    @Resource
    private UserEntityManage userEntityManage;

    @Resource
    private ResourceTypeEntityManage resourceTypeEntityManage;

    @Resource
    private ResourceTypeEntityDataManage resourceTypeEntityDataManage;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private TaskService taskService;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private HistoryService historyService;

    @Resource
    private ProcessEngine processEngine;

    @Resource
    private ManagementService managementService;

    @Resource
    private MessageService messageService;

    @Resource
    private ApplyManage applyManage;

    @Resource
    private ResourceCacheInfoManage resourceCacheInfoManage;

	@Resource
    private MailProxy mailProxy;

	@Value("${resourcevisualization.path.myApply}")
    private String myApplyPath;

    @Value("${resourcevisualization.path.auditApply}")
    private String auditApplyPath;    private Logger LOGGER = LoggerFactory.getLogger(ProcessServiceImpl.class);

    @Override
    public ReturnStructure submitApply(int applyId) {
        // 通过申请id获取申请信息
        Apply applyInfo = applyManage.getApplyInfoById(applyId);
        if (applyInfo == null) {
            return ReturnStructure.NOT_FOUND_APPLY();
        }
        String processInstanceId = StringUtils.isBlank(applyInfo.getProcessInstanceId()) ? startDeploymentAndProcessInstance() : applyInfo.getProcessInstanceId();
        // 提交申请
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        if (task == null) {
            return ReturnStructure.NOT_FOUND("该流程实例不存在", null);
        }
        // 设置执行人
        task.setAssignee(applyInfo.getApplyBy());
        taskService.complete(task.getId());

        // 设置流程实例id
        applyInfo.setProcessInstanceId(processInstanceId);
        applyInfo.setApplyStatus(ApplyProcessStatus.auditing);
        applyManage.updateProcessInstanceIdAndStatusById(applyInfo);

        // 通过邮箱发送信息给管理员
        mailProxy.sendTextMailToAdmin("资源申请",getUserRealName(applyInfo) + "刚提交了一条资源申请");

        return ReturnStructure.SUCCESS("提交申请成功", null);
    }

    @Override
    public ReturnStructure cancelApply(int applyId) {
        // 通过申请id获取到申请信息
        Apply applyInfo = applyManage.getApplyInfoById(applyId);
        if (applyInfo == null) {
            return ReturnStructure.NOT_FOUND_APPLY();
        }

        // 删除流程实例
        runtimeService.deleteProcessInstance(applyInfo.getProcessInstanceId(), "我不需要申请了");

        // 邮件通知管理员
        mailProxy.sendTextMailToAdmin("取消申请",getUserRealName(applyInfo) + "刚取消了一条资源申请");

        // 修改申请信息状态
        applyInfo.setApplyStatus(ApplyProcessStatus.waitSubmit);
        applyInfo.setProcessInstanceId("");
        applyManage.updateProcessInstanceIdAndStatusById(applyInfo);

        return ReturnStructure.SUCCESS("取消申请成功", null);
    }

    @Override
    public ReturnStructure auditApply(int applyId, boolean agree, String remark) {
        Apply applyInfo = applyManage.getApplyInfoById(applyId);
        if (applyInfo == null) {
            return ReturnStructure.NOT_FOUND_APPLY();
        }

        // 设置审批参数
        Map<String, Object> auditParam = new HashMap<>(1);
        if (agree) {
            // 邮件通知申请人
            mailProxy.sendTextMailByUsername(applyInfo.getApplyBy(),"申请审批通过","管理员通过了您的申请，正在分配资源中...");

            auditParam.put("agree", 1);            applyManage.updateApplyStatusById(applyId, ApplyProcessStatus.assigning);
        } else {
			// 邮件通知申请人
            mailProxy.sendTextMailByUsername(applyInfo.getApplyBy(),"申请审批未通过","管理员拒绝了您的申请,管理员备注为：" + remark);

            auditParam.put("agree", 0);            applyInfo.setAdminRemark(remark);
            applyInfo.setApplyStatus(ApplyProcessStatus.auditFail);
            applyManage.updateAdminRemarkAndStatusById(applyInfo);
        }

        // 执行审批
        taskService.complete(getTaskIdByProcessInstanceId(applyInfo.getProcessInstanceId()), auditParam);

        return ReturnStructure.SUCCESS("审批成功", null);
    }

    @Override
    public ReturnStructure assignResource(@NotNull ProcessDealDto processDealDto) {
        Apply applyInfo = applyManage.getApplyInfoById(processDealDto.getApplyId());
        if (applyInfo == null) {
            return ReturnStructure.NOT_FOUND_APPLY();
        }

        if (applyInfo.getApplyResourceId() == 0 && applyInfo.getResourceCacheInfoId() != 0 && !"pm".equals(applyInfo.getResourceTypeName())) {
            // 添加资源实体
            int entityId = addResourceEntity(resourceCacheInfoManage.getResourceCacheInfoById(applyInfo.getResourceCacheInfoId()));
            applyInfo.setApplyResourceId(entityId);
        }

        // 执行任务
        taskService.complete(getTaskIdByProcessInstanceId(applyInfo.getProcessInstanceId()));
        applyInfo.setApplyStatus(ApplyProcessStatus.using);
        applyInfo.setAdminRemark(processDealDto.getRemark());
        applyManage.updateAdminRemarkAndStatusById(applyInfo);

        // 分配使用人
        assignUserEntity(applyInfo);

        // 邮件发送信息
        mailProxy.sendTextMailByUsername(applyInfo.getApplyBy(),"资源分配成功","分配成功，资源可以使用了，管理员给您的反馈为：" + applyInfo.getAdminRemark());
if (applyInfo.getApplyResourceId() != 0 && applyInfo.getResourceCacheInfoId()==0){
            User userInfo = userManage.getUserByName(applyInfo.getApplyBy());

            ResourceTypeEntityData resourceTypeEntityData = new ResourceTypeEntityData();
            resourceTypeEntityData.setResourceTypeEntityId(applyInfo.getApplyResourceId());
            resourceTypeEntityData.setResourceTypeFieldName("group");
            resourceTypeEntityData.setResourceTypeEntityDataValue(userInfo.getUserGroupName());

            resourceTypeEntityDataManage.updateResourceTypeEntityDataByEntityIdAndFieldName(resourceTypeEntityData);
        }
        return ReturnStructure.SUCCESS("分配资源成功", null);
    }

    @Override
    public ReturnStructure rebackApply(@NotNull ProcessDealDto processDealDto) {
        Apply applyInfo = applyManage.getApplyInfoById(processDealDto.getApplyId());
        if (applyInfo == null) {
            return ReturnStructure.NOT_FOUND_APPLY();
        }
        try {
            managementService.executeCommand(new MoveNodeCommand(getTaskIdByProcessInstanceId(applyInfo.getProcessInstanceId()),systemConfigManage.getConfigByKey("waitSubmitNodeId").getValue()));
            applyInfo.setAdminRemark(processDealDto.getRemark());
            applyInfo.setApplyStatus(ApplyProcessStatus.auditFail);
            applyManage.updateAdminRemarkAndStatusById(applyInfo);
            mailProxy.sendTextMailByUsername(applyInfo.getApplyBy(),"申请被驳回","管理员拒绝了您的申请,管理员备注为：" + processDealDto.getRemark());        }catch (Exception e){
            LOGGER.error(e.getMessage());
            return ReturnStructure.ERROR("节点跳转失败",null);
        }

        return ReturnStructure.SUCCESS("驳回申请成功", null);
    }

    private void assignUserEntity(@NotNull Apply applyInfo) {
        UserEntity userEntity = new UserEntity();
        userEntity.setEntityId(applyInfo.getApplyResourceId());
        userEntity.setUsername(applyInfo.getApplyBy());
        userEntity.setUserGroupName(userManage.getUserByName(applyInfo.getApplyBy()).getUserGroupName());
        if (userEntityManage.getUserEntityByEntityId(userEntity.getEntityId()) != null) {
            userEntityManage.updateUserEntityByEntityId(userEntity);
        } else {
            userEntityManage.addUserEntity(userEntity);
        }
    }

    @Override
    public ReturnStructure returnResource(int applyId) {
        Apply applyInfo = applyManage.getApplyInfoById(applyId);
        if (applyInfo == null) {
            return ReturnStructure.NOT_FOUND_APPLY();
        }
        applyManage.updateApplyStatusById(applyId, ApplyProcessStatus.returning);

        taskService.complete(getTaskIdByProcessInstanceId(applyInfo.getProcessInstanceId()));

        mailProxy.sendTextMailToAdmin("归还资源",getUserRealName(applyInfo) + "发送归还资源请求");

        return ReturnStructure.SUCCESS("归还资源成功", null);
    }

    @Override
    public ReturnStructure freeResource(int applyId) {
        Apply applyInfo = applyManage.getApplyInfoById(applyId);
        if (applyInfo == null) {
            return ReturnStructure.NOT_FOUND_APPLY();
        }
        applyManage.updateApplyStatusById(applyId, ApplyProcessStatus.free);

        taskService.complete(getTaskIdByProcessInstanceId(applyInfo.getProcessInstanceId()));
        mailProxy.sendTextMailByUsername(applyInfo.getApplyBy(),"资源释放","您申请的资源已经释放");

        // 清除使用人
        userEntityManage.deleteUserEntityByEntityId(applyInfo.getApplyResourceId());

        return ReturnStructure.SUCCESS("释放资源成功", null);
    }

    @Override
    public ReturnStructure advanceFreeResource(int applyId, String adminRemark) {
        Apply applyInfo = applyManage.getApplyInfoById(applyId);
        if (applyInfo == null) {
            return ReturnStructure.NOT_FOUND_APPLY();
        }
        applyInfo.setAdminRemark(adminRemark);
        applyInfo.setApplyStatus(ApplyProcessStatus.free);
        applyManage.updateAdminRemarkAndStatusById(applyInfo);

        runtimeService.deleteProcessInstance(applyInfo.getProcessInstanceId(), adminRemark);
        mailProxy.sendTextMailByUsername(applyInfo.getApplyBy(),"资源释放","您申请的资源已经释放,管理员备注为：" + adminRemark);

        // 清除使用人
        userEntityManage.deleteUserEntityByEntityId(applyInfo.getApplyResourceId());

        return ReturnStructure.SUCCESS("释放资源成功", null);
    }

    @Override
    public List<String> getProcessInstanceIdListByAuditor() {
        List<String> processInstanceIdList = new ArrayList<>();
        List<Task> taskList = taskService.createTaskQuery().taskAssignee("管理员").list();
        for (Task eachTask : taskList) {
            processInstanceIdList.add(eachTask.getProcessInstanceId());
        }
        return processInstanceIdList;
    }

    @Override
    public ReturnStructure viewProcessImage(String processInstanceId, HttpServletResponse response) {
        try {
            InputStream diagram = getDiagram(processInstanceId);
            if (diagram == null) {
                return new ReturnStructure(400, "is查看流程图失败");
            }
            response.setContentType("image/png");

            BufferedImage image = ImageIO.read(diagram);
            OutputStream out = response.getOutputStream();
            ImageIO.write(image, "png", out);

            diagram.close();
            out.close();
            return null;
        } catch (Exception e) {
            return ReturnStructure.ERROR("查看流程图失败", null);
        }
    }

    /**
     * 循环发送暄暄通知
     *
     * @param message 要发送的信息
     */
    private void sendMessageToAdmin(String message) {
        String[] admins = systemConfigManage.getConfigByKey("receiveMessageAdmin").getValue().split(",");
        for (String admin : admins) {
            messageService.sendMessageToXuanXuan(admin, message,auditApplyPath);
        }
    }

    /**
     * 获取流程图流
     *
     * @param processInstanceId 流程实例id
     * @return 文件流
     */
    @Nullable
    private InputStream getDiagram(String processInstanceId) {
        //获得流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        String processDefinitionId;
        if (processInstance == null) {
            //查询已经结束的流程实例
            HistoricProcessInstance processInstanceHistory =
                    historyService.createHistoricProcessInstanceQuery()
                            .processInstanceId(processInstanceId).singleResult();
            if (processInstanceHistory == null) {
                return null;
            } else {
                processDefinitionId = processInstanceHistory.getProcessDefinitionId();
            }
        } else {
            processDefinitionId = processInstance.getProcessDefinitionId();
        }

        //使用宋体
        String fontName = "宋体";
        //获取BPMN模型对象
        BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
        //获取流程实例当前的节点，需要高亮显示
        List<String> currentActs = Collections.EMPTY_LIST;
        if (processInstance != null) {
            currentActs = runtimeService.getActiveActivityIds(processInstance.getId());
        }

        return processEngine.getProcessEngineConfiguration()
                .getProcessDiagramGenerator()
                .generateDiagram(model, "png", currentActs, new ArrayList<>(),
                        fontName, fontName, fontName, null, 1.0);
    }

    /**
     * 添加资源实体数据
     *
     * @param resourceCacheInfo 缓存资源信息
     */
    private int addResourceEntity(@NotNull ResourceCacheInfo resourceCacheInfo) {
        ResourceTypeEntity resourceTypeEntityInfo = getResourceTypeEntity(applyManage.getApplyInfoByResourceCacheInfoId(resourceCacheInfo.getId()));
        resourceTypeEntityManage.addResourceTypeEntity(resourceTypeEntityInfo);

        addResourceTypeEntityData(resourceTypeEntityInfo, JSONObject.parseObject(resourceCacheInfo.getResourceEntityInfo()));

        return resourceTypeEntityInfo.getResourceTypeEntityId();
    }

    /**
     * 添加动态字段数据
     *
     * @param resourceTypeEntity        资源类别实体数据
     * @param resourceTypeFieldDataJSON 资源类别字段列表数据JSON
     */
    private void addResourceTypeEntityData(ResourceTypeEntity resourceTypeEntity, @NotNull JSONObject resourceTypeFieldDataJSON) {
        for (String fieldName : resourceTypeFieldDataJSON.keySet()) {
            ResourceTypeEntityData resourceTypeEntityData = new ResourceTypeEntityData();
            resourceTypeEntityData.setResourceTypeName(resourceTypeEntity.getResourceTypeName());
            resourceTypeEntityData.setResourceTypeEntityId(resourceTypeEntity.getResourceTypeEntityId());
            resourceTypeEntityData.setResourceTypeFieldName(fieldName);
            resourceTypeEntityData.setResourceTypeEntityDataValue((String) resourceTypeFieldDataJSON.get(fieldName));
            resourceTypeEntityDataManage.addResourceTypeEntityData(resourceTypeEntityData);
        }
    }

    /**
     * 得到资源类别实体信息
     *
     * @param applyInfo 资源申请信息
     * @return 资源类别实体信息
     */
    @NotNull
    private ResourceTypeEntity getResourceTypeEntity(@NotNull Apply applyInfo) {
        ResourceTypeEntity resourceTypeEntityInfo = new ResourceTypeEntity();
        resourceTypeEntityInfo.setResourceTypeName(applyInfo.getResourceTypeName());
        resourceTypeEntityInfo.setResourceTypeEntityDescription(applyInfo.getApplyReason());
        resourceTypeEntityInfo.setStatus("using");
        resourceTypeEntityInfo.setCreatedBy(applyInfo.getApplyBy());
        return resourceTypeEntityInfo;
    }

    /**
     * 通过流程实例id获取任务
     *
     * @param processInstanceId 流程实例id
     * @return 任务id
     */
    private String getTaskIdByProcessInstanceId(String processInstanceId) {
        return taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult().getId();
    }

    /**
     * 部署流程并开启实例
     *
     * @return 流程实例id
     */
    private String startDeploymentAndProcessInstance() {
        // 获取流程部署id
        String processDeploymentId = systemConfigManage.getConfigByKey("processDeploymentId").getValue();
        // 获取流程定义id
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(processDeploymentId).singleResult();
        // 开启一个流程并获取流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId());
        return processInstance.getId();
    }

    /**
     * 获取用户真实名
     *
     * @param apply 申请人用户名
     * @return 申请人真实名
     */
    private String getUserRealName(@NotNull Apply apply) {
        return userManage.getUserByName(apply.getApplyBy()).getRealName();
    }
}
